package hzn.distributeLock;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.util.Collections;
import java.util.List;

public class MyLock {
    ZooKeeper zkClient;
    String lockPath;//向zk中写入的节点名称
    Watcher watcher;

    //1。产生客户端 2.产生监听器
    public MyLock()throws Exception{
        zkClient=new ZooKeeper("192.168.184.100:2181",10000,null);
        watcher=new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                System.out.println(event.getPath()+"锁已释放");
                synchronized (this){
                    this.notifyAll();
                }
            }
        };
    }

    //获取锁
    void acquireLock()throws Exception{
        //创建锁节点
        createLock();
        //尝试获取锁
        attemptLock();
    }
    void createLock()throws Exception{
        //判断父节点是否存在，不存在就创建
        Stat stat=zkClient.exists("/Locks",false);
        try{
            if(stat==null){
                zkClient.create("/Locks",new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        }catch (Exception e){
            System.out.println(Thread.currentThread().getName()+"创建Locks父目录不成功");
        }
        //开始创建子节点,/Locks/lock_000004
        lockPath=zkClient.create("/Locks/Lock_",
                Thread.currentThread().getName().getBytes(),
                ZooDefs.Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL_SEQUENTIAL);
    }

    void attemptLock()throws Exception{
        List<String>lockPaths= zkClient.getChildren("/Locks",false);//1分钟
        Collections.sort(lockPaths);
        int index=lockPaths.indexOf(lockPath.substring("/Locks".length()+1));
        //如果locksPath是序号最小的节点，则成功得到锁
        if(index==0){
            System.out.println(Thread.currentThread().getName()+"获得锁，lockPath:"+lockPath);
            return;
        }else {//不是序号最小，则监控前一节点
            String preLockPath=lockPaths.get(index-1);
            Stat stat=zkClient.exists("/Locks/"+preLockPath,watcher);
            if(stat==null){
                //有两种可能导致前任不存在：
                // 1.前任的业务执行完后主动删除该节点
                //2.前任的服务器崩溃
                attemptLock();
            }else {
                System.out.println("等待前锁释放，preLockPath:"+preLockPath);
                synchronized (watcher){
                    watcher.wait();
                }
                attemptLock();
            }
        }
    }
    public void releaseLock()throws Exception{
        zkClient.delete(lockPath,-1);//-1表示不管该节点版本是什么，都删除
        zkClient.close();
    }
}
